/* 提供业务处理的功能, 处理来自客户端的各种请求 */
#ifndef __MY_SERVICE__
#define __MY_SERVICE__
#include "DataManager.hpp"
#include "log.hpp"
#include "httplib.h"

extern cloud::DataManager* dm;         // 用于管理数据
extern Log my_log;                     // 日志类

namespace cloud
{
class Service
{
private:
    int _srvPort;                    // 服务器端口号
    std::string _srvIp;              // 服务器ip
    std::string _urlDownPre;         // url下载路径前缀
public:
    Service()
    {
        cloud::Config* cf = cloud::Config::getInstance();
        _srvIp = cf->getServerIp();
        _srvPort = cf->getServerPort();
        _urlDownPre = cf->getDownPre();   
        // dm = new cloud::DataManager();
    }

    ~Service()
    {
        // delete dm;
    }

    // 进行业务处理
    void runService()
    {
        httplib::Server srv;            // 服务器
        srv.Post("/upload", uploadHandler);
        srv.Get("/list", listHandler); 
        srv.Get("/", listHandler);     // 默认进行显示操作
        std::string down = _urlDownPre+"(.*)";     // 可以接受/urlDownPre/.* 表示任意名称都可以
        srv.Get(down, downloadHandler);
        srv.listen(_srvIp, _srvPort);
    }

private:
    // 处理上传文件
    static void uploadHandler(const httplib::Request& req, httplib::Response& rsp)
    {
        auto ret = req.has_file("file");        // 是否上传了1个名为file的文件
        if(ret == false) {
            my_log(ERROR, "no file found in void uploadHandler(const httplib::Request&, httplib::Response&)\n");
            // std::cerr << "上传文件失败！\n";
            rsp.status = 404;
            return;
        }
        
        // 上传文件成功
        const auto& file = req.get_file_value("file");      // 获取该文件
        // ulFilePath类似于./files/aaa.txt, 放的是文件实际在服务器存储的位置
        std::string ulFilePath = cloud::Config::getInstance()->getUlPath() + file.filename; 
        // 打开该文件, 向其写入内容, 不存在就会创建
        cloud::FileUtil fu(ulFilePath);

        if(fu.setContent(file.content) == false) {
            my_log(ERROR, "write file content error in void uploadHandler(const httplib::Request&, httplib::Response&)\n");
            // std::cerr << "写入文件内容失败！\n";
            rsp.status = 500;
            return;
        }

        // 创建BackupInfo对象
        cloud::BackupInfo bi(ulFilePath);      
        if(bi.isOk() == false) {
            my_log(ERROR, "BackupInfo not ok~ in void uploadHandler(const httplib::Request&, httplib::Response&)\n");
            // std::cerr << "void uploadHandler(const httplib::Request&, httplib::Response&)\nBackupInfo not ok~\n";
            return;
        }

        // 向dm管理的哈希表插入数据
        if(dm->insertInfo(bi) == false) {
            my_log(ERROR, "insert info error in void uploadHandler(const httplib::Request&, httplib::Response&)\n");
            // std::cerr << "void uploadHandler(const httplib::Request&, httplib::Response&)\ninsert info error\n";
            return;
        }
        
        // std::cout << "beore sotrageBackup()\n";

        // 持久化操作
        if(dm->insertToDatabase(bi) == false) {
            my_log(ERROR, "insertToDatabase error in void uploadHandler(const httplib::Request&, httplib::Response&)\n");
            // std::cerr << "void uploadHandler(const httplib::Request&, httplib::Response&)\nstrorage backup error\n";
            // std::cout << "after sotrageBackup()\n";
            return;
        }
        // 所有操作成功后返回JSON响应
        rsp.status = 200;  // 设置成功状态码
        rsp.set_header("Content-Type", "application/json");  // 设置响应格式
        rsp.body = R"({"success": true, "message": "文件上传成功"})";  //  JSON响应体
    }   

    // 处理下载文件
    static void downloadHandler(const httplib::Request& req, httplib::Response& rsp)
    {
        std::string urlPath = req.path;        // 获取文件的url资源路径
        BackupInfo info;
        std::string oldETag, newETag;          // 客户端那里的旧的ETag和服务器这里新的ETag, 判断是否相等, 如果不相等，断点续传就得重新从头开始下载

        // 获取文件内容
        if(dm->get1FromURL(urlPath, &info) == false) {
            return;
        } else {
            info.biStatus = true;
            newETag = getETag(info);
        }
        
        // 文件如果被压缩, 就要解压后删除压缩包, 并更改属性
        if(info.isPacked == true) {
            cloud::FileUtil fu(info.rarFilePath);
            if(fu.unCompress(info.ulFilePath) == false)     return;
            info.isPacked = false;                              // 文件不再被压缩
            info.lastATime = info.lastMTime = time(NULL);       // 文件最后一次访问时间和修改时间更新
            if(fu.removeFile() == false)                    return;
            // 更新备份信息
            dm->updateInfo(info);
            dm->strorageBackup();
        }

        // 读取文件内容
        std::string content;
        cloud::FileUtil fu(info.ulFilePath);
        if(fu.getFullContent(&content) == false)            return;

        bool isResume = false;      // 是否对区间的请求, true表示进行是, 后面需要设置返回码

        if(req.has_header("If-Range")) {
            oldETag = req.get_header_value("If-Range");
            // 有If-Range字段且这个字段的值与请求文件的最新Etag一致则符合断点续传
            if(oldETag == newETag)     isResume = true;     
        }

        // 设置rsp, 如果是对区间的请求, httplib库已经处理好了, 进需要设置body即可
        rsp.body = content;
        rsp.set_header("Content-Type", "application/octet-stream");     // 响应头部: 二进制流数据，用于文件下载
        rsp.set_header("Accept-Ranges", "bytes");                       // 服务器支持基于字节的请求, 断点续传需要这个
        rsp.set_header("ETag", newETag);
        rsp.status = ((isResume == true) ? 206 : 200);
        // if(isResume == true)    rsp.status = 206;
        // else rsp.status = 200;
    }

    // 处理显示操作
    static void listHandler(const httplib::Request& req, httplib::Response& rsp)
    {
        std::stringstream ss;
        ss << "<html><head><title>Download</title></head><body><h1>Download</h1><table>";
        std::vector<cloud::BackupInfo> array;

        // 获取文件所有的信息
        if(dm->getAllInfo(&array) == false) {
            std::cerr << "void listHandler(const httplib::Request&, httplib::Response&)\nget dll info error\n";
            return;
        }

        for(const auto& a : array) {
            ss << "<tr>";
            // todo: byte KB MB
            std::string fileName = cloud::FileUtil(a.ulFilePath).getFileName();     // 获得文件名
            ss << "<td><a href=\"" << a.urlPath << "\">  " << fileName << "  </a>" << "</td>";
            ss << "<td align=\"right\">  " << timeToStr(a.lastATime) << "  </td>";
            ss << "<td align=\"right\">  " << a.fileSz/1024 << "KB  </td>";
            ss << "</tr>";
        }

        ss << "</table></body></html>";

        rsp.body = ss.str();                            // 设置正文
        rsp.set_header("Content-Type", "text/html");    // 设置响应头部
        rsp.status = 200;                               // 设置状态行的状态码
    }

    // 将时间戳转换为可读的
    static std::string timeToStr(time_t t) 
    {
        return std::ctime(&t);
    }

    // 获取ETag, 文件名-文件大小-最后一次修改时间
    static std::string getETag(const BackupInfo& bi)
    {
        std::stringstream ss;
        ss << cloud::FileUtil(bi.ulFilePath).getFileName() 
        << "+" << bi.fileSz 
        << "+" << bi.lastMTime;
        return ss.str();
    }
};
}
#endif